package com.iflytek.jzcpx.procuracy.common.aspect;

import javax.validation.ConstraintViolation;
import javax.validation.Validator;
import javax.validation.executable.ExecutableValidator;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import com.iflytek.jzcpx.procuracy.common.enums.CommonResultEnum;
import com.iflytek.jzcpx.procuracy.common.exception.InvalidParameterException;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;

/**
 * 参数验证 AOP
 *
 * @author <a href=mailto:ktyi@iflytek.com>伊开堂</a>
 * @date 2019/3/4 09:52
 */
@Aspect
@Component
public class ValidationAspect {

    @Autowired
    private Validator validator;

    /**
     * service层非法入参校验
     *
     * @param point 切点
     *
     * @throws InvalidParameterException 检测到非法入参时抛出
     */
    // @Before("execution(public * com.iflytek.jzcpx.procuracy.*.service..*Service.*(..))")
    public void before(JoinPoint point) {
        List<String> errors = new ArrayList<>();

        Object[] args = point.getArgs();
        if (ArrayUtils.isNotEmpty(args)) {
            //方法级别校验
            ExecutableValidator executableValidator = validator.forExecutables();
            Method method = ((MethodSignature) point.getSignature()).getMethod();

            Set<ConstraintViolation<Object>> violationSet = executableValidator.validateParameters(point.getThis(), method, args);
            if (CollectionUtils.isNotEmpty(violationSet)) {
                errors.addAll(violationSet.stream().map(ConstraintViolation::getMessage).collect(Collectors.toList()));
            }

            //获取annotation，Validated进行bean级别校验
            Annotation[][] annotations = method.getParameterAnnotations();
            for (int i = 0; i < annotations.length; i++) {
                Annotation[] annotationArr = annotations[i];
                for (Annotation annotation : annotationArr) {
                    if (annotation.annotationType().equals(Validated.class)) {
                        Class<?>[] validatedGroups = ((Validated) annotation).value();
                        Set<ConstraintViolation<Object>> violations = new HashSet<>();
                        // 先验证无分组字段
                        CollectionUtils.addAll(violations, validator.validate(args[i]));

                        // 在验证有分组字段
                        if (args[i] != null) {
                            CollectionUtils.addAll(violations, validator.validate(args[i], validatedGroups));
                        }

                        if (CollectionUtils.isNotEmpty(violations)) {
                            errors.addAll(violations.stream().map(ConstraintViolation::getMessage).collect(Collectors.toList()));
                            break;
                        }
                    }
                }
            }

            if (CollectionUtils.isNotEmpty(errors)) {
                String errorMsg = String.join("; ", errors);
                throw new InvalidParameterException(CommonResultEnum.INVALID_PARAMETER, errorMsg);
            }
        }
    }

}
